package utils;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;

import org.apache.http.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * http请求客户端工具
 * @author ljc
 * @version 1.0
 */
public class HttpClientUtils {
	private static Logger log= LoggerFactory.getLogger(HttpClientUtils.class);
	private static final String parameterJoin = "&";
    private static final String parameterEqual = "=";
    
    /** 输出日志级别：全部输出 */
    public static final int PRINTLOGLEVEL_ALL=3;
    /** 输出日志级别：只输出返回结果 */
    public static final int PRINTLOGLEVEL_RESPONSE=2;
    /** 输出日志级别：只输出请求 */
    public static final int PRINTLOGLEVEL_REQUEST=1;
    /** 输出日志级别：不输出 */
    public static final int PRINTLOGLEVEL_NO=0;
	 
    public static String appendParameter(String baseUrl,
             Map<String, String> parameterMap) {
			StringBuffer parasb = new StringBuffer(baseUrl);
			if(baseUrl.indexOf("?")<0)
			{
				parasb.append("?");
			}
			if (null != parameterMap) {
			parasb.append(parameterJoin);
			for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
			String val;
			try {
			val = URLEncoder.encode(entry.getValue(), "UTF-8");
			} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			val = entry.getValue();
			}
			parasb.append(entry.getKey()).append(parameterEqual)
			.append(val)
			.append(parameterJoin);
			
			}
			}
			return parasb.substring(0, parasb.length() - 1);
	}
 
	 
    /**
     * get请求方法
     * @param url 地址
     * @param params 参数
     * @return
     */
	public static BaseResult<InputStream> get(String url, Map<String, String> params){
			BaseResult<InputStream> result=null;
	        CloseableHttpClient httpclient = null;
	        CloseableHttpResponse response = null;
	        log.info("请求URL:>>>" + url + "调用参数：>>>>" + params);
			byte[] fileBytes = null;
	        try {
	        	if(url.toLowerCase().startsWith("https:"))
				{
					httpclient=httpsClient();
				}else{
					httpclient= HttpClients.createDefault();
				}
	            String newUrl = appendParameter(url, params);
	            HttpGet httpGet = new HttpGet(newUrl);
	            response = httpclient.execute(httpGet);
		        int statusCode = response.getStatusLine().getStatusCode();
		        InputStream imageFileInputStream = response.getEntity().getContent();
		        Header contentType = response.getEntity().getContentType();
		        if (contentType != null && contentType.getValue() != null && contentType.getValue().contains("application/json")) {

			        imageFileInputStream = null;
		        }
		        if (imageFileInputStream != null) {
			        fileBytes = IOUtils.toByteArray(imageFileInputStream);
		        }

	            result=new BaseResult<>();
	            result.setCode(result.CODE_SUCCESS);
	            result.setMsg("success");
	            result.setData(imageFileInputStream);
	            return result;
	        } catch (Exception e) {
	        	log.error("HttpClient请求get方法异常", e);
	        	result=new BaseResult<>();
	        	result.setCode(result.CODE_FAILE);
	        	result.setMsg(e.getMessage());
	        }catch (Throwable e) {
				log.error("HttpClient请求get方法异常",e);
				result=new BaseResult<>();
				result.setCode(result.CODE_FAILE);
	        	result.setMsg(e.getMessage());
	        } finally {
	            try {
	                response.close();
	                httpclient.close();
	            } catch (Exception ignore) {
	            }
	        }
	        return result;
	    }
	
	public static BaseResult<String> post(String url, Map<String, String> params){
		return post(url,params,PRINTLOGLEVEL_ALL);
	}
 
	/**
	 * post请求
	 * @param url
	 * @param params
	 * @param printLogLevel 输出日志级别：1只输出请求日志，2只输出返回日志，3请求和返回日志都输出
	 * @return
	 */
	public static BaseResult<String> post(String url, Map<String, String> params,int printLogLevel){
		if(printLogLevel==PRINTLOGLEVEL_REQUEST || printLogLevel ==PRINTLOGLEVEL_ALL)
		{
			log.info("请求URL:>>>" + url + "调用参数：>>>>" + params);
		}
		BaseResult<String> result=null;
		CloseableHttpClient httpclient =null;
		CloseableHttpResponse response = null;
		try {
			if(url.toLowerCase().startsWith("https:"))
			{
				httpclient=httpsClient();
			}else{
				httpclient= HttpClients.createDefault();
			}
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
			for (String key : params.keySet()) {
				nameValuePairs
						.add(new BasicNameValuePair(key, params.get(key)));
			}
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));

			response = httpclient.execute(httpPost);
			InputStream inputStream = response.getEntity().getContent();
			BufferedReader rd = new BufferedReader(new InputStreamReader(
					inputStream));

			String line = "";
			StringBuffer resultString = new StringBuffer();
			while ((line = rd.readLine()) != null) {
				resultString.append(line);
			}
			
			String responseContent=resultString.toString();
			if(printLogLevel==PRINTLOGLEVEL_RESPONSE || printLogLevel ==PRINTLOGLEVEL_ALL)
			{
				log.info("请求" + url + "回复>>>>>:"+ responseContent);
			}
			
			result=new BaseResult<String>();
            result.setCode(result.CODE_SUCCESS);
            result.setMsg("success");
            result.setData(responseContent);
            return result;
		} catch (Exception e) {
			log.error("HttpClient请求post方法异常", e);
			result=new BaseResult<String>();
			result.setCode(result.CODE_FAILE);
        	result.setMsg(e.getMessage());
		}catch (Throwable e) {
			log.error("HttpClient请求post方法异常",e);
			result=new BaseResult<String>();
			result.setCode(result.CODE_FAILE);
        	result.setMsg(e.getMessage());
		} finally {
			try {
				response.close();
				httpclient.close();
			} catch (Exception ignore) {
			}
		}
		return result;
	}
	
	public static String post(String url,String jsonStr,int printLogLevel){
		String result = null;
		if(printLogLevel==PRINTLOGLEVEL_REQUEST || printLogLevel==PRINTLOGLEVEL_ALL)
		{
			log.info("请求URL:>>>" + url + "参数：>>>>" + jsonStr);
		} 
		
		CloseableHttpClient httpclient =null;
		CloseableHttpResponse response = null;
		try {
			if(url.toLowerCase().startsWith("https:"))
			{
				httpclient=httpsClient();
			}else{
				httpclient= HttpClients.createDefault();
			}
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(new StringEntity(jsonStr, "utf-8"));
			response = httpclient.execute(httpPost);
			if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity());
				if(printLogLevel==PRINTLOGLEVEL_RESPONSE || printLogLevel==PRINTLOGLEVEL_ALL)
				{
					log.info("请求URL({}) 返回结果：{}",url,result);
				}
				
			}else{
				log.error("HttpClient post error,return status"+response.getStatusLine().getStatusCode());
				throw new Exception("http请求错误,错误码:"+response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			log.error("HttpClient post error", e);
		}catch (Throwable e) {
			log.error("HttpClient post error",e);
		} finally {
			try {
				response.close();
				httpclient.close();
			} catch (Exception e) {
				log.error("HttpClient close error",e);
			}
		}
		return result;
	}
	
	
	/**
	 * @param url
	 * @param hearderParams
	 * @param jsonStr
	 * @param printLogLevel 输出日志级别：1只输出请求日志，2只输出返回日志，3请求和返回日志都输出
	 * @return
	 */
	public static String post(String url,Map<String,String> hearderParams,String jsonStr,int printLogLevel){
		String result = null;
		if(printLogLevel==PRINTLOGLEVEL_REQUEST || printLogLevel==PRINTLOGLEVEL_ALL)
		{
			log.info("请求URL:>>>" + url + "参数：>>>>" + jsonStr);
		} 
		
		CloseableHttpClient httpclient =null;
		CloseableHttpResponse response = null;
		try {
			if(url.toLowerCase().startsWith("https:"))
			{
				httpclient=httpsClient();
			}else{
				httpclient= HttpClients.createDefault();
			}
			HttpPost httpPost = new HttpPost(url);
			for (Map.Entry<String, String> entry : hearderParams.entrySet()) {
				httpPost.addHeader(entry.getKey(), entry.getValue());
			}
			httpPost.setEntity(new StringEntity(jsonStr, "utf-8"));
			response = httpclient.execute(httpPost);
			if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity());
				if(printLogLevel==PRINTLOGLEVEL_RESPONSE || printLogLevel==PRINTLOGLEVEL_ALL)
				{
					log.info("请求URL({}) 返回结果：{}",url,result);
				}
				
			}else{
				log.error("HttpClient post error,return status"+response.getStatusLine().getStatusCode());
				throw new Exception("http请求错误,错误码:"+response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			log.error("HttpClient post error", e);
		}catch (Throwable e) {
			log.error("HttpClient post error",e);
		} finally {
			try {
				response.close();
				httpclient.close();
			} catch (Exception e) {
				log.error("HttpClient close error",e);
			}
		}
		return result;
	}	
	public static String post(String proxyIp,int proxyPort,String url,Map<String,String> hearderParams,String jsonStr){
		String result = null;
		log.info("请求URL:>>>" + url + "(网络代理："+proxyIp+":"+proxyPort+")参数：>>>>" + jsonStr);
		CloseableHttpClient httpclient =null;
		CloseableHttpResponse response = null;
		try {
			if(url.toLowerCase().startsWith("https:"))
			{
				httpclient=httpsClient();
			}else{
				httpclient=socks4ProxyClient(proxyIp, proxyPort);
			}
			HttpPost httpPost = new HttpPost(url);
			for (Map.Entry<String, String> entry : hearderParams.entrySet()) {
				httpPost.addHeader(entry.getKey(), entry.getValue());
			}
			httpPost.setEntity(new StringEntity(jsonStr, "utf-8"));
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(70000).setConnectTimeout(70000).build();//设置请求和传输超时时间 
			httpPost.setConfig(requestConfig);
			response = httpclient.execute(httpPost);
			if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity());
				log.info("请求URL({}) 返回结果：{}",url,result);
				
			}else{
				log.error("HttpClient post error,return status"+response.getStatusLine().getStatusCode());
				throw new Exception("http请求错误,错误码:"+response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			log.error("HttpClient post error", e);
		}catch (Throwable e) {
			log.error("HttpClient post error",e);
		} finally {
			try {
				response.close();
				httpclient.close();
			} catch (Exception e) {
				log.error("HttpClient close error",e);
			}
		}
		return result;
	}	
	
	/**
	 * Post请求
	 * @param url 地址
	 * @param params 参数
	 * @return
	 */
	public static String postReturnStr(String url, Map<String, String> params){
		String result=null;
		CloseableHttpClient httpclient =null;
		CloseableHttpResponse response = null;
		try {
			if(url.toLowerCase().startsWith("https:"))
			{
				httpclient=httpsClient();
			}else{
				httpclient= HttpClients.createDefault();
			}
			HttpPost httpPost = new HttpPost(url);
			if(params != null){
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
				for (String key : params.keySet()) {
					nameValuePairs
							.add(new BasicNameValuePair(key, params.get(key)));
				}
				httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
			}
			response = httpclient.execute(httpPost);
			InputStream inputStream = response.getEntity().getContent();
			BufferedReader rd = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));  
			String line = "";
			StringBuffer resultString = new StringBuffer();
			while ((line = rd.readLine()) != null) {
				resultString.append(line);
			}			
			result = resultString.toString();
		} catch (Exception e) {
			log.error("HttpClient请求post方法异常", e);
		}catch (Throwable e) {
			log.error("HttpClient请求post方法异常",e);
		} finally {
			try {
				response.close();
				httpclient.close();
			} catch (Exception ignore) {
			}
		}
		return result;
	}	
	
	 
	
	/**
	 * 流数据请求
	 * @return
	 */
	public static BaseResult<byte[]> getInputStream(String reqUrl)
	{
		log.info("请求URL:>>>" + reqUrl );
		BaseResult<byte[]> result=null;
		ByteArrayOutputStream baos = null; 
		InputStream is = null;
		try {
			final URL url = new URL(reqUrl);
			final URLConnection con = url.openConnection();
			con.setDoOutput(true);
			con.setDoInput(true);
			con.setUseCaches(false);
			con.setRequestProperty("Content-Type", "application/octet-stream");
			is = con.getInputStream();
			baos = new ByteArrayOutputStream();  
			byte[] buffer = new byte[1024];  
			int len = -1;
			while ((len = is.read(buffer)) != -1) {
				baos.write(buffer, 0, len);
			}
			result=new BaseResult<byte[]>();
            result.setCode(result.CODE_SUCCESS);
            result.setMsg("success");
            result.setData(baos.toByteArray());
		} catch (Exception e) {
			log.error("HttpClient请求getInputStream方法异常", e);
			result=new BaseResult<byte[]>();
			result.setCode(result.CODE_FAILE);
        	result.setMsg(e.getMessage());
		} finally {
			try {
				baos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	 
	}
	  

	private static CloseableHttpClient httpsClient() throws Throwable {

		SSLContext sslcontext = new SSLContextBuilder().loadTrustMaterial(null,
				new TrustStrategy() {
					// 信任所有
					public boolean isTrusted(X509Certificate[] chain,
							String authType) throws CertificateException {
						return true;
					}
				}).build();

		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
    			.register("http", PlainConnectionSocketFactory.INSTANCE)
    			.register("https", sslsf)
    			.build();
		
		CookieStore cookieStore = new BasicCookieStore();
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		connManager.setMaxTotal(110);	
		
		 
		CloseableHttpClient httpclient = HttpClients.custom()
													.setSSLSocketFactory(sslsf)
													.setConnectionManager(connManager)
													.setDefaultCookieStore(cookieStore).build();

		
		return httpclient;

	}

	private static CloseableHttpClient socks4ProxyClient(String ip,int port) throws Throwable {

		 Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory> create()
	                .register("http", new Socks4ProxyConnectionSocketFactory(ip,port)).build();
	        // HTTP客户端连接管理池
	        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(reg);
	        CloseableHttpClient httpclient = HttpClients.custom()
	                    .setConnectionManager(connManager)
	                    .build();
		
		return httpclient;

	}
	
	private  static class Socks4ProxyConnectionSocketFactory implements ConnectionSocketFactory {
		   private String ip;
		   private int port;
			public Socks4ProxyConnectionSocketFactory(String ip,int port)
			{
				this.ip=ip;
				this.port=port;
			}
	        @Override
	        public Socket createSocket(final HttpContext context) throws IOException {
	        	   InetSocketAddress socksaddr = new InetSocketAddress(ip,port);
//	            InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
	            // socket代理
	            Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
	            return new Socket(proxy);
	        }
	 
	        @Override
	        public Socket connectSocket(
	                final int connectTimeout,
	                final Socket socket,
	                final HttpHost host,
	                final InetSocketAddress remoteAddress,
	                final InetSocketAddress localAddress,
	                final HttpContext context) throws IOException, ConnectTimeoutException {
	            Socket sock;
	            if (socket != null) {
	                sock = socket;
	            } else {
	                sock = createSocket(context);
	            }
	            if (localAddress != null) {
	                sock.bind(localAddress);
	            }
	            try {
	                sock.connect(remoteAddress, connectTimeout);
	            } catch (SocketTimeoutException ex) {
	                throw new ConnectTimeoutException(ex, host, remoteAddress.getAddress());
	            }
	            return sock;
	        }
	 
	    }
	
	/**
	 * GET 请求
	 * @note only using for liuymf
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static String requestGet(String url) throws  Exception{
		log.info("requestGet URL:"+url);
		String responseHtml = null;
		CloseableHttpClient closeableHttpClient  = getCloseableHttpClient();
		HttpGet httpGet = new HttpGet(url);
		HttpResponse httpResponse = closeableHttpClient.execute(httpGet);
		if(httpResponse !=null){
			responseHtml = EntityUtils.toString(httpResponse.getEntity());
			log.info("requestGet responseHtml:"+responseHtml);
		}
		closeableHttpClient.close();
		return  responseHtml;
	}


	/**
	 * 获取  CloseableHttpClient
	 * @note only using for liuymf
	 * @return
	 * @throws Exception
	 */
	private static CloseableHttpClient getCloseableHttpClient() throws  Exception{
		SSLContextBuilder builder = new SSLContextBuilder();
		builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				builder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		CloseableHttpClient closeableHttpClient = HttpClients.custom().setSSLSocketFactory(
				sslsf).build();
		return closeableHttpClient;
	}
}
